<?php

/**
 * 存储及搜索服务配置及实例化类接口
 *
 * @package    modules
 * @subpackage Common
 * @author     suning <snsnsky@gmail.com>
 *
 * $Id$
 */

namespace Common;

use \lib\plugins\pdo\PDOQuery;
use \lib\plugins\cache\Memcached;
use \lib\plugins\cache\RedisCache;
use \lib\search\ElasticSearch as ESClient;
use \lib\plugins\cache\FileCache;
use lib\plugins\cache\MongodbCache;

class Bridge
{
    // {{{
    ///////////////////// db ///////////////////////
    /**
     * DB/REDIS 读写属性: 掩码
     *
     * @var integer
     */
    const DB_MASK = 0xF0;
    /**
     * DB/REDIS 读写属性: 读
     *
     * @var integer
     */
    const DB_MASK_RERAD = 0x00;
    /**
     * DB/REDIS 读写属性: 写
     *
     * @var integer
     */
    const DB_MASK_WRITE = 0x01;

    /**
     * 数据库类型: 用户读库
     *
     * @var integer
     */
    const DB_USER_R = 0x10;
    /**
     * 数据库类型: 用户写库
     *
     * @var integer
     */
    const DB_USER_W = 0x11;
    /**
     * 数据库类型: 个人中心读库
     *
     * @var integer
     */
    const DB_CENTER_R = 0x20;
    /**
     * 数据库类型: 个人中心写库
     *
     * @var integer
     */
    const DB_CENTER_W = 0x21;
    /**
     * 数据库类型: 后台读库
     *
     * @var integer
     */
    const DB_ADMIN_R = 0x30;
    /**
     * 数据库类型: 后台写库
     *
     * @var integer
     */
    const DB_ADMIN_W = 0x31;
    /**
     * 数据库类型: Mobile读库
     *
     * @var integer
     */
    const DB_MOBILE_R = 0x40;
    /**
     * 数据库类型: Mobile写库
     *
     * @var integer
     */
    const DB_MOBILE_W = 0x41;
    /**
     * 数据库类型: 活动读库
     *
     * @var integer
     */
    const DB_ACT_R = 0x50;
    /**
     * 数据库类型: 活动写库
     *
     * @var integer
     */
    const DB_ACT_W = 0x51;

    /**
     * 数据库类型: 特卖读库
     *
     * @var integer
     */
    const DB_PASSPORT_R = 0x60;

    /**
     * 数据库类型: 特卖写库
     *
     * @var integer
     */
    const DB_PASSPORT_W = 0x61;

    /**
     * REDIS类型: 用户读库
     *
     * @var integer
     */

    ///////////////////// redis ///////////////////////
    const REDIS_USER_R = 0x10;
    /**
     * REDIS类型: 用户写库
     *
     * @var integer
     */
    const REDIS_USER_W = 0x11;

    /**
     * REDIS类型: APP读库
     *
     * @var integer
     */
    const REDIS_MOBILE_R = 0x20;
    /**
     * REDIS类型: APP写库
     *
     * @var integer
     */
    const REDIS_MOBILE_W = 0x21;
    /**
     * REDIS类型: 队列读库
     *
     * @var integer
     */
    const REDIS_QUEUE_R = 0x30;
    /**
     * REDIS类型: 队列写库
     *
     * @var integer
     */
    const REDIS_QUEUE_W = 0x31;
    /**
     * REDIS类型: Other读库
     *
     * @var integer
     */
    const REDIS_OTHER_R = 0x40;
    /**
     * REDIS类型: Other写库
     *
     * @var integer
     */
    const REDIS_OTHER_W = 0x41;

    /**
     * REDIS类型: 消息读库
     *
     * @var integer
     */
    const REDIS_NOTICE_R = 0x50;
    /**
     * REDIS类型: 消息写库
     *
     * @var integer
     */
    const REDIS_NOTICE_W = 0x51;

    ///////////////////// memcache ///////////////////////
    /**
     * Memcache类型: 默认
     *
     * @var integer
     */
    const MEMCACHE_DEFAULT = 0x10;

    ///////////////////// mongodb ///////////////////////
    /**
     * MongoDB类型: 默认
     *
     * @var integer
     */
    const MONGODB_DEFAULT = 0x10;
    const MONGODB_TEMAI = 0x11;

    ///////////////////// es ///////////////////////
    /**
     * 搜索类型: 默认
     *
     * @var integer
     */
    const SEARCH_DEFAULT = 0x10;
    // }}}

    // {{{
    protected static $_db_cfgs = [
        self::DB_USER_R => ['name' => 'default', 'db' => 'jg_user'],
        self::DB_USER_W => ['name' => 'default', 'db' => 'jg_user'],
        self::DB_CENTER_R => ['name' => 'default', 'db' => 'jg_center'],
        self::DB_CENTER_W => ['name' => 'default', 'db' => 'jg_center'],
        self::DB_ADMIN_R => ['name' => 'default', 'db' => 'jg_admin'],
        self::DB_ADMIN_W => ['name' => 'default', 'db' => 'jg_admin'],
        self::DB_MOBILE_R => ['name' => 'default', 'db' => 'jg_mobile'],
        self::DB_MOBILE_W => ['name' => 'default', 'db' => 'jg_mobile'],
        self::DB_ACT_R => ['name' => 'default', 'db' => 'jg_act'],
        self::DB_ACT_W => ['name' => 'default', 'db' => 'jg_act'],
        self::DB_PASSPORT_R => ['name' => 'default', 'db' => 'jg_passport'],
        self::DB_PASSPORT_W => ['name' => 'default', 'db' => 'jg_passport'],
    ];
    protected static $_redis_cfgs = [
        self::REDIS_USER_R => 'user',
        self::REDIS_USER_W => 'user',
        self::REDIS_QUEUE_R => 'queue',
        self::REDIS_QUEUE_W => 'queue',
        self::REDIS_OTHER_R => 'other',
        self::REDIS_OTHER_W => 'other',
        self::REDIS_MOBILE_R => 'mobile',
        self::REDIS_MOBILE_W => 'mobile',
        self::REDIS_NOTICE_R => 'notice',
        self::REDIS_NOTICE_W => 'notice',
    ];
    protected static $_memcache_cfgs = [
        self::MEMCACHE_DEFAULT => 'default',
    ];
    protected static $_search_cfgs = [
        self::SEARCH_DEFAULT => 'default',
    ];
    protected static $_mongodb_cfgs = [
        self::MONGODB_DEFAULT => ['name' => 'default', 'db' => 'user'],
    ];
    // }}}


    /**
     * 返回PDO数据库连接实例
     *
     * @param mixed $params  连接参数
     * @param mixed $options 选项
     * @return PDOQuery|\PDOStatement|bool
     */
    public static function pdo($params, $options = null)
    {
        static $_static = [];

        if ($options === 'force_close') {
            $keys = array_keys($_static);

            foreach ($keys as $key) {
                unset($_static[$key]);
            }

            return true;
        }

        $key = md5(json_encode([$params, $options]));

        if (!isset($_static[$key])) {
            if (is_numeric($params)) {
                $configs = self::getMysqlConfig();
                if (isset($configs[$params])) {
                    $params = $configs[$params];
                }
            }

            self::_checkConnectionParams([$params], 'mysql');

            $_static[$key] = new PDOQuery($params, $options, function () use (&$_static, $key) {
                unset($_static[$key]);
            });
        } else {
            if (method_exists($_static[$key], 'clear')) {
                $_static[$key]->clear();
            }
        }

        return $_static[$key];
    }

    /**
     * 获取 MySQL 服务的配置
     *
     * @return array
     */
    public static function getMysqlConfig()
    {
        static $_static = [];

        if (empty($_static)) {
            $configs = self::_loadConfigFile('mysql');

            foreach (self::$_db_cfgs as $key => $meta) {
                $name = $meta['name'];

                if (isset($configs[$name])) {
                    $conn = $configs[$name];

                    $cfg = $conn[($key & self::DB_MASK_WRITE) ? 'w' : 'r'];

                    $cfg['name'] = $meta['db'];
                    $cfg['driver'] = 'mysql';
                    if (isset($meta['charset'])) {
                        $cfg['charset'] = $meta['charset'];
                    }
                    if (!isset($cfg['port'])) {
                        $cfg['port'] = 3306;
                    }

                    $_static[$key] = $cfg;
                }
            }
        }

        return $_static;
    }

    protected static function _loadConfigFile($type)
    {
        static $_static = [];

        if (empty($_static)) {
            $_static = require(BASEPATH_CONFIG . 'bridge.cfg.php');
        }

        return $_static[$type];
    }

    /**
     * 检查连接参数
     *
     * @param array  $param_arr
     * @param string $type
     * @return bool
     * @throws \Exception
     */
    protected static function _checkConnectionParams($param_arr, $type)
    {
        static $_filters = [
            'port' => [
                'filter' => FILTER_VALIDATE_INT,
            ],
            'db' => [
                'filter' => FILTER_VALIDATE_REGEXP,
                'options' => [
                    'regexp' => '/^[a-z0-9_]{1,64}$/i',
                ],
            ],
            'index' => [
                'filter' => FILTER_VALIDATE_INT,
            ],
        ];

        $valid_params = false;

        foreach ($param_arr as $params) {
            $valid_params = false;

            if (is_array($params)) {
                $valid_params = true;
                $params = filter_var_array($params, $_filters);
                foreach ($params as $param) {
                    if ($param === false) {
                        $valid_params = false;
                        break;
                    }
                }
            }

            if (!$valid_params) {
                break;
            }
        }

        if (!$valid_params) {
            throw new \Exception("Invalid Connection Parameters Of {$type}");
        }

        return true;
    }

    /**
     * 返回Redis连接实例
     *
     * @param mixed   $params  连接参数
     * @param integer $dbindex 库号
     * @param integer $timeout 超时时间
     * @return RedisCache
     */
    public static function redis($params, $dbindex = 0, $timeout = 5)
    {
        static $_static = [];

        $dbindex = (is_numeric($dbindex) && ($dbindex = (int)$dbindex) > 0) ? $dbindex : 0;
        $key = md5(json_encode([$params, $dbindex]));

        if (!isset($_static[$key])) {
            if (is_numeric($params)) {
                $configs = self::getRedisConfig();
                if (isset($configs[$params])) {
                    $params = $configs[$params];
                }
            }

            self::_checkConnectionParams([$params], 'redis');

            $_static[$key] = new RedisCache($params, $dbindex, $timeout);
        }

        return $_static[$key];
    }

    /**
     * 获取 Redis 服务的配置
     *
     * @return array
     */
    public static function getRedisConfig()
    {
        static $_static = [];

        if (empty($_static)) {
            $configs = self::_loadConfigFile('redis');

            foreach (self::$_redis_cfgs as $key => $name) {
                if (isset($configs[$name])) {
                    $conn = $configs[$name];

                    $_static[$key] = $conn[($key & self::DB_MASK_WRITE) ? 'w' : 'r'];
                }
            }
        }

        return $_static;
    }

    /**
     * 返回Memcache连接实例
     *
     * @param mixed   $params  连接参数
     * @param integer $timeout 超时时间
     * @return Memcached
     */
    public static function memcache($params = self::MEMCACHE_DEFAULT, $timeout = 5)
    {
        static $_static = [];

        $key = md5(json_encode([$params]));
        if (!isset($_static[$key])) {
            if (is_numeric($params)) {
                $configs = self::getMemcacheConfig();
                if (isset($configs[$params])) {
                    $params = $configs[$params];
                }
            }

            self::_checkConnectionParams($params, 'memcache');

            if(__ENV__ == 'DEV' && !class_exists('Memcached')) {
                $no_memcached = get_cfg_var('no_memcached');
                if ($no_memcached) {
                    $_static[$key] = new \lib\plugins\cache\MemcacheDEV();
                }
            } else {
                $_static[$key] = new Memcached($params, '', $timeout, $key);
            }
        }

        return $_static[$key];
    }

    /**
     * 获取 Memcached 服务的配置
     *
     * @return array
     */
    public static function getMemcacheConfig()
    {
        static $_static = [];

        if (empty($_static)) {
            $configs = self::_loadConfigFile('memcache');

            foreach (self::$_memcache_cfgs as $key => $val) {
                if (isset($configs[$val])) {
                    $_static[$key] = $configs[$val];
                }
            }
        }

        return $_static;
    }

    /**
     * 返回MongoDB连接实例
     *
     * @param mixed $params  连接参数
     * @param array $options 额外参数
     * @return \MongoDB\Client
     */
    public static function mongodb($params = self::MONGODB_DEFAULT, $options = [])
    {
        static $_static = [];

        $key = md5(json_encode([$params, $options]));
        if (!isset($_static[$key])) {
            if (is_numeric($params)) {
                $configs = self::getMongoDBConfig();
                if (isset($configs[$params])) {
                    $params = $configs[$params];
                }
            }

            $_static[$key] = new MongodbCache($params, $options);
        }

        return $_static[$key];
    }

    /**
     * 获取 MongoDB 服务的配置
     *
     * @return array
     */
    public static function getMongoDBConfig()
    {
        static $_static = [];

        if (empty($_static)) {
            $configs = self::_loadConfigFile('mongodb');

            foreach (self::$_mongodb_cfgs as $key => $val) {
                if (isset($configs[$val['name']])) {
                    $cfg = $configs[$val['name']];
                    $cfg['db'] = $val['db'];
                    $_static[$key] = $cfg;
                }
            }
        }

        return $_static;
    }

    /**
     * 返回搜索连接实例
     *
     * @param mixed   $params  连接参数
     * @param integer $timeout 超时时间
     * @return ESClient
     */
    public static function search($params = self::SEARCH_DEFAULT, $timeout = 2)
    {
        static $_static = [], $included = false;

        $key = md5(json_encode([$params]));
        if (!isset($_static[$key])) {
            if (!$included) {
                $included = true;
            }

            if (is_numeric($params)) {
                $configs = self::getSearchConfig();
                if (isset($configs[$params])) {
                    $params = $configs[$params];
                }
            }

            $params = (array)$params;
            if (isset($params['servers'])) {
                $servers = $params['servers'];
            } else {
                $sub_param = current($params);

                if (is_array($sub_param) && isset($sub_param['host'])) {
                    $servers = $params;
                    $params = [
                        'servers' => $servers,
                    ];
                } else {
                    $servers = [$params];
                }
            }

            self::_checkConnectionParams($servers, 'search');

            $params['timeout'] = (is_numeric($timeout) && ($timeout = (int)$timeout) > 0) ? $timeout : 2;
            $_static[$key] = new ESClient($params);
        }

        return $_static[$key];
    }

    /**
     * 获取搜索服务的配置
     *
     * @return array
     */
    public static function getSearchConfig()
    {
        static $_static = [];

        if (empty($_static)) {
            $configs = self::_loadConfigFile('search');

            foreach (self::$_search_cfgs as $key => $val) {
                if (isset($configs[$val])) {
                    $_static[$key] = $configs[$val];
                }
            }
        }

        return $_static;
    }

    /**
     * 返回文件缓存实例
     *
     * @param string $path 缓存路径
     * @return FileCache
     */
    public static function filecache($path)
    {
        static $_static = [];

        $key = md5($path);
        if (!isset($_static[$key])) {
            $_static[$key] = new FileCache($path);
        }

        return $_static[$key];
    }
}
